/*
 * $Id: XLIReader.cc,v 1.6 2006-02-10 05:10:12 bacon Exp $
 */

#include <xpp/xli/XLIReader.h>
#include <xpp/ScopedPtr.h>
#include <xp/bas/ctype.h>

#define READ_CHAR() \
	do { if (read_char() == -1) return -1; } while (0)

#define READ_TOKEN() \
	do { if (read_token() == -1) return XP_NULL; } while(0)

namespace xpp
{
	namespace xli
	{

		XLIReader::XLIReader ()
		{
			//read_char ();
			clear ();
		}
		
		XLIReader::~XLIReader ()
		{
		}

		void XLIReader::clear ()
		{
			this->error_code = ERR_NONE;
			_line    = 0;
			_col     = 0;
			this->token_line = 0;
			this->token_col  = 0;

			this->cur_char = XP_CHAR_EOF;
			this->cur_line = 1;
			this->cur_col  = 1;
		}
		
		/*
		 * program    = cell_list
		 * list       = [cell]*
		 * cell       = key '=' val
		 * val        = str_val | list_val
		 * str_val    = str ';' 
		 * str        = [single_str]+
		 * single_str = '"' characters '"'
		 * list_val   = '{' list '}'
		 */
		
		XLI* XLIReader::read ()
		{
			clear ();
			if (read_char() == -1) return XP_NULL;
	
			try {
				READ_TOKEN();
				return read_list(false);
				/*
				ScopedPtr<XLI> xliPtr (read_list(false));
				return xliPtr.release ();	
				*/
			}
			/*
			catch (Err&) {
				return XP_NULL;
			}
			*/
			catch (...) {
				this->error_code = ERR_EXCEPTION;
				return XP_NULL;
			}
		}
		
		XLI* XLIReader::read_list (bool inList)
		{
			ScopedPtr<XLI> xliPtr (new XLI());
			xliPtr->setLine (this->token_line);
			xliPtr->setCol  (this->token_col);
		
			for (;;) {
				if (token.type == Token::T_END) break;
				if (inList && token.type == Token::T_RBRACE) break;

				XLICell* cell = read_cell();
				if (cell == XP_NULL) return XP_NULL;

				xliPtr->insert (cell);
			}
		
			return xliPtr.release();
		}
		
		XLICell* XLIReader::read_cell ()
		{
			XLIKey* key = read_key ();
			if (key == XP_NULL) return XP_NULL;

			ScopedPtr<XLIKey> keyPtr (key);
			if (token.type != Token::T_ASSIGN) {
				this->error_code = ERR_ASSIGNOP;
				return XP_NULL;
			}
		
			READ_TOKEN ();
		
			XLIVal* val = read_val ();
			if (val == XP_NULL) return XP_NULL;
			ScopedPtr<XLIVal> valPtr (val);
			ScopedPtr<XLICell> cellPtr (new XLICell());
			cellPtr->set (keyPtr.release(), valPtr.release());
			return cellPtr.release ();
		}
		
		XLIKey* XLIReader::read_key ()
		{
			if (token.type != Token::T_SYMBOL) {
				this->error_code = ERR_SYMBOL;
				return XP_NULL;
			}
			if (token.name.length() > XLIKey::MAX_DATA_LEN) {
				this->error_code = ERR_SYMBOLTOOLONG;
				return XP_NULL;
			}
		
			ScopedPtr<XLIKey> keyPtr (
				new XLIKey ((const xp_char_t*)token.name)
			);
			keyPtr->setLine (this->token_line);
			keyPtr->setCol  (this->token_col);
		
			READ_TOKEN ();
			return keyPtr.release ();	
		}
		
		XLIVal* XLIReader::read_val ()
		{
			if (token.type == Token::T_STRING) {
				ScopedPtr<XLIStr> valPtr (
					new XLIStr ((const xp_char_t*)token.name)
				);
				valPtr->setLine (this->token_line);
				valPtr->setCol  (this->token_col);
		
				READ_TOKEN ();
				if (token.type != Token::T_SEMICOLON) {
					this->error_code = ERR_SEMICOLON;
					return XP_NULL;
				}

				READ_TOKEN ();
				return valPtr.release ();
			}
			else if (token.type == Token::T_LBRACE) {
				READ_TOKEN ();
		
				XLI* xli = read_list(true);
				if (xli == XP_NULL) return XP_NULL;

				ScopedPtr<XLI> valPtr (xli);
				if (token.type != Token::T_RBRACE) {
					this->error_code = ERR_RBRACE;
					return XP_NULL;
				}

				READ_TOKEN ();
				return valPtr.release ();
			}
		
			this->error_code = ERR_VALUE;
			return XP_NULL;
		}
		
		int XLIReader::read_token ()
		{
			int n;

			token.clear ();
		
			for (;;) {
				if (skip_spaces() == -1) return -1;
				n = skip_comment ();
				if (n == -1) return -1;
				if (n == 0) break;
			}

			xp_cint_t c = this->cur_char;
		
			_line = this->cur_line;
			_col = this->cur_col;
			if (_col > 1) _col--;
		
			this->token_line = _line;
			this->token_col  = _col;
		
			/*if (c == EzStream::Error) {
				this->error_code = ERR_SYSTEM;
				throw Err ();
			}
			else*/ if (c == XP_CHAR_EOF)  {
				token.set (Token::T_END); 
			}
			else if (xp_isalpha(c) || c == XP_CHAR('_')) {
				if (read_symbol() == -1) return -1;
			}
			else if (c == XP_CHAR('\'') || c == XP_CHAR('\"')) {
				if (read_string(c, false) == -1) return -1;
				for (;;) {
					for (;;) {
						if (skip_spaces () == -1) return -1;
						n = skip_comment ();
						if (n == -1) return -1;
						if (n == 0) break;
					}

					if (this->cur_char != XP_CHAR('\'') &&
					    this->cur_char != XP_CHAR('\"')) break;

					if (read_string(this->cur_char, true) == -1) return -1;
				}
			}
			else if (c == XP_CHAR('{')) {
				READ_CHAR ();
				token.set (Token::T_LBRACE, XP_TEXT("{"));
			}
			else if (c == XP_CHAR('}')) {
				READ_CHAR ();
				token.set (Token::T_RBRACE, XP_TEXT("}"));
			}
			else if (c == XP_CHAR('=')) {
				READ_CHAR ();
				token.set (Token::T_ASSIGN, XP_TEXT("="));
			}
			else if (c == XP_CHAR(';')) {
				READ_CHAR ();
				token.set (Token::T_SEMICOLON, XP_TEXT(";"));
			}
			else {
				this->error_code = ERR_WRONGCHAR;
				return -1;
			}

			return 0;
		}
		
		int XLIReader::read_symbol ()
		{
			token.set (Token::T_SYMBOL);
		
			for (;;) {
				if (!xp_isalnum (this->cur_char) && 
				    this->cur_char != XP_CHAR('_')) break;
				
				token.name += this->cur_char;
				READ_CHAR ();
			}

			return 0;
		}
		
		int XLIReader::read_string (xp_char_t start, bool continued)
		{
			xp_cint_t c;
			bool esc = false;
		
			if (!continued) {
				//when the previous token is not a string
				token.type = Token::T_STRING;
				token.name = XP_TEXT("");
			}
		
			READ_CHAR ();

			for (;;) {
				c = this->cur_char;
				if (c == XP_CHAR_EOF) {
					token.type = Token::T_ERROR;
					token.name = XP_TEXT("");
					break;
				}
				if (esc == false && c == start) {
					READ_CHAR ();
					break;
				}
				if (esc == false && c == XP_CHAR('\\')) {
					READ_CHAR ();
					esc = true;
					continue;
				}
		
				if (esc == true) {
					if (c == XP_CHAR('n')) c = XP_CHAR('\n');
					else if (c == XP_CHAR('t')) c = XP_CHAR('\t');
					esc = false;
				}
				token.name += c;
				READ_CHAR ();
			}

			return 0;
		}
		
		int XLIReader::skip_spaces ()
		{
			for (;;) {
				if (this->cur_char == XP_CHAR_EOF) break;
				if (!xp_isspace(this->cur_char) && 
				    this->cur_char != XP_CHAR('\n')) break;
				READ_CHAR();
			}

			return 0;
		}
		
		int XLIReader::skip_comment ()
		{
			if (this->cur_char == XP_CHAR('#')) {
				for (;;) {
					READ_CHAR ();
					if (this->cur_char == XP_CHAR('\n') ||
					    this->cur_char == XP_CHAR_EOF) break;
				}	

				return 1; 
			}
		
			return 0;
		}
		
		int XLIReader::read_char ()
		{
			xp_char_t c;

			int n = this->readChar (&c);
			if (n < 0) {
				this->error_code = ERR_READCHAR;
				return -1;
			}

			if (n == 0) {
				this->cur_char = XP_CHAR_EOF;
				return 0;
			}

			this->cur_char = c;
			if (this->cur_char == XP_CHAR('\n')) {
				this->cur_line++;
				this->cur_col = 1;
			}
			else this->cur_col++;
			return 0;
		}

		const xp_char_t* XLIReader::errorStr () const
		{
			static const xp_char_t* __error_str[] =
			{
				XP_TEXT("no error"),
				XP_TEXT("cannot read character"),
				XP_TEXT("assignemt operator expected"),
				XP_TEXT("symbol expected"),
				XP_TEXT("symbol too long"),
				XP_TEXT("semicolon expected"),
				XP_TEXT("right brace expected"),
				XP_TEXT("value expected"),
				XP_TEXT("wrong character"),
				XP_TEXT("system error"),
				XP_TEXT("exception")
			};
		
			return (this->error_code >= (int)xp_countof(__error_str))?
				XP_TEXT("unknown error"):
				__error_str[this->error_code];
		}

	}
}
